/**
 * server.js - 服务器入口文件
 * 
 * 这个文件是应用程序的入口点，负责启动HTTP服务器，
 * 初始化数据库连接，并设置未捕获异常和拒绝承诺的处理程序。
 */

// 加载环境变量
require('dotenv').config();

// 导入依赖
const http = require('http');
const app = require('./app');
const config = require('./config');
const logger = require('./utils/logger');
const { testConnection } = require('./config/database');
const { initUserTable } = require('./models/userModel');

/**
 * 规范化端口值
 * 
 * 将端口值转换为数字、字符串或false
 * @param {string|number} val - 端口值
 * @returns {number|string|boolean} - 规范化的端口值
 */
const normalizePort = (val) => {
  const port = parseInt(val, 10);

  if (isNaN(port)) {
    return val; // 命名管道
  }

  if (port >= 0) {
    return port; // 端口号
  }

  return false;
};

/**
 * 获取端口
 * 
 * 从环境变量或配置中获取端口号
 */
const port = normalizePort(config.app.port || '3000');
app.set('port', port);

/**
 * 创建HTTP服务器
 */
const server = http.createServer(app);

/**
 * 错误事件监听器
 * 
 * 处理服务器"错误"事件
 * @param {Error} error - 错误对象
 */
const onError = (error) => {
  if (error.syscall !== 'listen') {
    throw error;
  }

  const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`;

  // 处理特定的监听错误，并提供友好的错误消息
  switch (error.code) {
    case 'EACCES':
      logger.error(`${bind} 需要提升权限`);
      process.exit(1);
      break;
    case 'EADDRINUSE':
      logger.error(`${bind} 已被占用`);
      process.exit(1);
      break;
    default:
      throw error;
  }
};

/**
 * 监听事件处理程序
 * 
 * 处理服务器"监听"事件
 */
const onListening = () => {
  const addr = server.address();
  const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`;
  logger.info(`服务器已启动，监听端口 ${port}`);
  logger.info(`API文档可在 http://localhost:${port}/api-docs 访问`);
};

/**
 * 初始化数据库并启动服务器
 * 
 * 测试数据库连接，初始化表，然后启动服务器
 */
const startServer = async () => {
  try {
    // 测试数据库连接
    const dbConnected = await testConnection();
    
    if (dbConnected) {
      // 初始化用户表
      await initUserTable();
      
      // 启动服务器
      server.listen(port);
      server.on('error', onError);
      server.on('listening', onListening);
    } else {
      logger.error('无法启动服务器：数据库连接失败');
      process.exit(1);
    }
  } catch (error) {
    logger.error('启动服务器时出错:', error);
    process.exit(1);
  }
};

/**
 * 处理未捕获的异常
 */
process.on('uncaughtException', (err) => {
  logger.error('未捕获的异常:', err);
  process.exit(1);
});

/**
 * 处理未处理的承诺拒绝
 */
process.on('unhandledRejection', (reason, promise) => {
  logger.error('未处理的承诺拒绝:', reason);
});

/**
 * 启动服务器
 */
startServer();